7033. Вложенные множества

 

Петя недавно узнал о понятии множества и вложенности множеств. Он заинтересовался последовательностями S0 Ê S1 Ê  S2 ÊÊ Sn, у которых каждый следующий элемент – подмножество предыдущего, S0 – некоторое конечное множество {a1, a2, …, am}.

Петя отметил: каждому из вложенных множеств можно поставить в соответствие целое число H(S) =  . Таким образом H(S0) = 2m – 1, H(Ø) = 0.

Петя произвольным образом выбрал n чисел: h1, h2, …, hn. Ему стало интересно: сколько существует разных наборов вложенных множеств S1, S2, …, Sn  при H(S1) = h1 (mod 41), H(S2) = h2 (mod 41), …, H(Sn) = hn (mod 41)? К сожалению, Петя сбился со счета, поэтому просит Вас о помощи.

 

Вход. Первая строка содержит два целых числа n и m (1 ≤ n ≤ 5, 1 ≤ m ≤ 20). Вторая строка содержит n целых чисел h1, h2, …, hn, каждое из которых удовлетворяет условию: 0 ≤ hk ≤ 40.

 

Выход. Вывести  искомое количество вложенных множеств. Известно, что это число не превосходит 1018.

 

Пример входа 1

3 5

7 3 3

Пример входа 2

3 5

7 4 5

Пример входа 3

1 7

3

Пример входа 4

3 10

31 29 17

Пример выхода 1

1

Пример выхода 2

0

Пример выхода 3

4

Пример выхода 4

28

 

 

РЕШЕНИЕ

динамическое программирование - маски подмножеств

 

Анализ алгоритма

Пусть F(ni, Si) равно количеству вложенных множеств Si Ê  Si+1 ÊÊ Sn, для которых H(Si+1) (mod 41) = hi+1, H(Si+2) (mod 41) = hi+2, …, H(Sn) (mod 41) = hn.

Например, F(1, Sn-1) равно количеству вложенных множеств Sn-1 Ê  Sn, для которых H(Sn) (mod 41) = hn. Положим F(0, Sn) = 1 (имеется одно множество Sn без дополнительных ограничений на него).

Тогда количеством вложенных множеств S0 Ê S1 Ê  S2 ÊÊ Sn будет значение F(n, S0) = F(n, 2m – 1). Здесь 2m – 1 является маской множества S0 = {a1, a2, …, am}.

Задача решается генерацией всех возможных искомых последовательностей. Для вычисления F(n, S0) перебираем все подмножества S1 множества S0. Для каждого подмножества, для которого H(S1) % 41 = h1, далее решаем подзадачу F(n – 1, S1), которая ищет количество вложенных n множеств, наибольшее из которых равно S1. Отсюда

F(n, S0) =

Хранение значений функции F в ячейках массива f[n][2m] не пройдет по памяти при заданных ограничениях на n и m. Значения F(ni, Si) будут ненулевыми только для таких подмножеств Si, для которых H(Si) (mod 41) = hi. Поэтому F(ni, H(Si)) можно хранить в f[ni][ H(Si) / 41]. И тогда нам достаточно будет массива размером f[n][2m / 41].

В тестах могут встречаться значения hi = 0, на их обработку следует обратить внимание.

 

Пример

1. Существует только один вариант: S1 = {a1, a2, a3}, S2 = S3 = {a1, a2}. H(S1) = 7, H(S2) = H(S3) = 3.

2. Такой комбинации вложенных множеств не существует.

3. H({a1, a2}) = 21 – 1 + 22 – 1 = 3, H({a3, a4, a6}) = 22 + 23 + 25 = 4 + 8 + 32 = 44 = 41 + 3, Н({a1, a3, a5, a7}) = 20 + 22 + 24 + 26 = 1 + 4 + 16 + 64 = 85 = 2 · 41 + 3, Н({a2, a3, a4, a5, a6, a7}) = 21 + 22 + 23 + 24 + 25 + 26 = 2 + 4 + 8 + 16 + 32 + 64 = 126 = 3 · 41 + 3.

4. Представлено на рисунке.

 

Реализация алгоритма

Объявим используемые массивы.

 

int h[5];

long long f[6][(1 << 20) / 41 + 10];

 

Вычислим значение F(n, mask).

 

long long F(int n, int mask)

{

  if (n == 0) return 1;

  int m = mask / 41;

  if (f[n][m] != -1) return f[n][m];

  f[n][m] = 0;

 

Перебираем все подмаски sub_mask маски mask. Поскольку возможны hi = 0, то следует обрабатывать и нулевые подмаски.

 

  for (int sub_mask = mask; ; sub_mask = (sub_mask - 1) & mask)

  {

    if (sub_mask % 41 == h[N - n])

      f[n][m] += F(n - 1, sub_mask);

    if (sub_mask == 0) break;

  }

  return f[n][m];

}

 

Основная часть программы.Читаем входные данные.

 

scanf("%d %d", &N, &M);

for (i = 0; i < N; i++)

  scanf("%d", &h[i]);

memset(f, -1, sizeof(f));

 

Выводим ответ задачи F(n, 2m – 1).

 

printf("%lld\n", F(N, (1 << M) - 1));